#include "component.h"
#include <winsock2.h>

int getMP4Subformat(FERange range, void *context, FEList *subs);

static FEFormat format_mp4 = {0, getMP4Subformat};
FEFormat *get_format(){
    return &format_mp4;
}

int getMP4Subformat(FERange range, void *context, FEList *subs) {
    return 0;
}

#define FETCH_FIELD(range, offset, len, data, sub) \
    data = fetchFERangeByte(&range, offset, len); \
    if(!data) return -1; \
    subFERangeByte(&range, &sub, offset, len); \
    offset += len;

int64_t dissectMP4(FERange range, void *context, FEList *nodes);
int64_t dissectBox(FERange range, void *context, FEList *nodes);
int64_t dissectContainer(FERange range, void *context, FEList *nodes);
int64_t dissectFTYP(FERange range, void *context, FEList *nodes);
int64_t dissectMVHD(FERange range, void *context, FEList *nodes);
int64_t dissectTKHD(FERange range, void *context, FEList *nodes);
int64_t dissectMDHD(FERange range, void *context, FEList *nodes);
int64_t dissectHDLR(FERange range, void *context, FEList *nodes);
int64_t dissectVMHD(FERange range, void *context, FEList *nodes);
int64_t dissectSMHD(FERange range, void *context, FEList *nodes);
int64_t dissectDREF(FERange range, void *context, FEList *nodes);
int64_t dissectURI(FERange range, void *context, FEList *nodes);
int64_t dissectSTSD(FERange range, void *context, FEList *nodes);
int64_t dissectAVC1(FERange range, void *context, FEList *nodes);
int64_t dissectMP4A(FERange range, void *context, FEList *nodes);
int64_t dissectAVCC(FERange range, void *context, FEList *nodes);
int64_t dissectESDS(FERange range, void *context, FEList *nodes);
int64_t dissectSTSS(FERange range, void *context, FEList *nodes);
int64_t dissectSTSZ(FERange range, void *context, FEList *nodes);
int64_t dissectSTCO(FERange range, void *context, FEList *nodes);
int64_t dissectSTSC(FERange range, void *context, FEList *nodes);
int64_t dissectSTSCEntry(FERange range, void *context, FEList *nodes);

static FEField field_mp4 = {"MP4", 0, false, dissectMP4, nullptr};
static FEField field_pad = {"Pad", 0, false, nullptr, nullptr};
static FEField field_entry_count = {"Entry Count", 0, false, nullptr, nullptr};
static FEField field_box = {"Box", 0, false, dissectBox, nullptr};
static FEField field_box_size = {"Size", 0, false, nullptr, nullptr};
static FEField field_box_type = {"Type", 0, false, nullptr, displayString};
static FEField field_box_version = {"Version", 0, false, nullptr, nullptr};
static FEField field_box_flags = {"Flags", 0, false, nullptr, nullptr};
static FEField field_box_data = {"Data", 0, false, nullptr, nullptr};
static FEField field_hd_creation_time = {"Creation Time", 0, false, nullptr, nullptr};
static FEField field_hd_modification_time = {"Modification Time", 0, false, nullptr, nullptr};
static FEField field_hd_time_scale = {"Time scale", 0, false, nullptr, nullptr};
static FEField field_hd_duration = {"Duration", 0, false, nullptr, nullptr};
static FEField field_hd_volume = {"Volume", 0, false, nullptr, nullptr};
static FEField field_hd_matrix = {"Matrix", 0, false, nullptr, nullptr};
static FEField field_ftyp = {"File Type Box", 0, false, dissectFTYP, nullptr};
static FEField field_ftyp_major_brand = {"Major brand", 0, false, nullptr, displayString};
static FEField field_ftyp_minor_version = {"Minor version", 0, false, nullptr, nullptr};
static FEField field_ftyp_compatible_brand = {"Compatible brand", 0, false, nullptr, displayString};
static FEField field_moov = {"Movie Box", 0, false, dissectContainer, nullptr};
static FEField field_mvhd = {"Movie Header Box", 0, false, dissectMVHD, nullptr};
static FEField field_mvhd_rate = {"Rate", 0, false, nullptr, nullptr};
static FEField field_mvhd_pre_defined = {"Pre-defined", 0, false, nullptr, nullptr};
static FEField field_mvhd_next_track_id = {"Next Track ID", 0, false, nullptr, nullptr};
static FEField field_iods = {"Object Descriptor Box", 0, false, dissectBox, nullptr};
static FEField field_trak = {"Track Box", 0, false, dissectContainer, nullptr};
static FEField field_tkhd = {"Track Header Box", 0, false, dissectTKHD, nullptr};
static FEField field_tkhd_track_id = {"Track ID", 0, false, nullptr, nullptr};
static FEField field_tkhd_layer = {"Layer", 0, false, nullptr, nullptr};
static FEField field_tkhd_alternate_group = {"Alternate Group", 0, false, nullptr, nullptr};
static FEField field_tkhd_width = {"Width", 0, false, nullptr, nullptr};
static FEField field_tkhd_height = {"Height", 0, false, nullptr, nullptr};
static FEField field_mdia = {"Media Box", 0, false, dissectContainer, nullptr};
static FEField field_mdhd = {"Media Header Box", 0, false, dissectMDHD, nullptr};
static FEField field_mdhd_language = {"Language", 0, false, nullptr, nullptr};
static FEField field_mdhd_quality = {"Quality", 0, false, nullptr, nullptr};
static FEField field_hdlr = {"Handler Reference Box", 0, false, dissectHDLR, nullptr};
static FEField field_hdlr_type = {"Handler Type", 0, false, nullptr, displayString};
static FEField field_hdlr_name = {"Handler Name", 0, false, nullptr, displayString};
static FEField field_minf = {"Media Information Box", 0, false, dissectContainer, nullptr};
static FEField field_vmhd = {"Video Media Header Box", 0, false, dissectVMHD, nullptr};
static FEField field_vmhd_graphics_mode = {"Graphics Mode", 0, false, nullptr, nullptr};
static FEField field_vmhd_op_color = {"Operation Color", 0, false, nullptr, nullptr};
static FEField field_smhd = {"Sound Media Header Box", 0, false, dissectSMHD, nullptr};
static FEField field_smhd_banlance = {"Banlance", 0, false, nullptr, nullptr};
static FEField field_dinf = {"Data Information Box", 0, false, dissectContainer, nullptr};
static FEField field_dref = {"Data Reference Box", 0, false, dissectDREF, nullptr};
static FEField field_stbl = {"Sample Table Box", 0, false, dissectContainer, nullptr};
static FEField field_stsd = {"Sample Description Box", 0, false, dissectSTSD, nullptr};
static FEField field_stts = {"Decoding Time to Sample Box", 0, false, dissectBox, nullptr};
static FEField field_stss = {"Sync Sample Box", 0, false, dissectSTSS, nullptr};
static FEField field_stss_sample_number = {"Sample Number", 0, false, nullptr, nullptr};
static FEField field_stsc = {"Sample to Chunk Box", 0, false, dissectSTSC, nullptr};
static FEField field_stsc_entry = {"Entry", 0, false, dissectSTSCEntry, nullptr};
static FEField field_stsc_first_chunk = {"First Chunk", 0, false, nullptr, nullptr};
static FEField field_stsc_samples_per_chunk = {"Samples Per Chunk", 0, false, nullptr, nullptr};
static FEField field_stsc_sample_description_index = {"Sample Description Index", 0, false, nullptr, nullptr};
static FEField field_stsz = {"Sample Size Box", 0, false, dissectSTSZ, nullptr};
static FEField field_stsz_sample_size = {"Sample Size", 0, false, nullptr, nullptr};
static FEField field_stsz_sample_count = {"Sample Count", 0, false, nullptr, nullptr};
static FEField field_stsz_entry_size = {"Entry Size", 0, false, nullptr, nullptr};
static FEField field_stco = {"Chunk Offset Box", 0, false, dissectSTCO, nullptr};
static FEField field_stco_chunk_offset = {"Chunk Offset", 0, false, nullptr, nullptr};
static FEField field_udta = {"User Data Box", 0, false, dissectContainer, nullptr};
static FEField field_meta = {"The Meta Box", 0, false, dissectBox, nullptr};
static FEField field_ilst = {"Metedata Item List Box", 0, false, dissectContainer, nullptr};
static FEField field_mdat = {"Media Data Box", 0, false, dissectBox, nullptr};
static FEField field_url  = {"URL Box", 0, false, dissectURI, nullptr};
static FEField field_urn  = {"URN Box", 0, false, dissectURI, nullptr};
static FEField field_uri_location = {"Location", 0, false, nullptr, displayString};
static FEField field_avc1  = {"AVC1", 0, false, dissectAVC1, nullptr};
static FEField field_vse_width  = {"Width", 0, false, nullptr, nullptr};
static FEField field_vse_heigh  = {"Heigh", 0, false, nullptr, nullptr};
static FEField field_vse_horizontal_resolution  = {"Horizontal Resolution", 0, false, nullptr, displayHex};
static FEField field_vse_vertical_resolution  = {"Vertical Resolution", 0, false, nullptr, displayHex};
static FEField field_vse_frame_count  = {"Frame Count", 0, false, nullptr, nullptr};
static FEField field_vse_compressor_name  = {"Compressor Name", 0, false, nullptr, displayString};
static FEField field_vse_depth  = {"Depth", 0, false, nullptr, displayHex};
static FEField field_avcc  = {"AVC Decoder Configuration Box", 0, false, dissectAVCC, nullptr};
static FEField field_avcc_configuration_version  = {"Configuration Version", 0, false, nullptr, nullptr};
static FEField field_avcc_profile_indication  = {"Profile Indication", 0, false, nullptr, nullptr};
static FEField field_avcc_profile_compatibility  = {"Profile Compatibility", 0, false, nullptr, nullptr};
static FEField field_avcc_level_indication  = {"Level Indication", 0, false, nullptr, nullptr};
static FEField field_avcc_length_size  = {"Length Size(Minus One)", 0, false, nullptr, nullptr};
static FEField field_avcc_number_sps  = {"Number of SPS", 0, false, nullptr, nullptr};
static FEField field_avcc_sps_length  = {"SPS Length", 0, false, nullptr, nullptr};
static FEField field_avcc_sps  = {"SPS", 0, false, nullptr, nullptr};
static FEField field_avcc_number_pps  = {"Number of PPS", 0, false, nullptr, nullptr};
static FEField field_avcc_pps_length  = {"PPS Length", 0, false, nullptr, nullptr};
static FEField field_avcc_pps  = {"PPS", 0, false, nullptr, nullptr};
static FEField field_esds  = {"Elementary Stream Descriptor Box", 0, false, dissectESDS, nullptr};
static FEField field_mp4a  = {"MP4A", 0, false, dissectMP4A, nullptr};
static FEField field_ase_channel_count  = {"Channel Count", 0, false, nullptr, nullptr};
static FEField field_ase_sample_size  = {"Sample Size", 0, false, nullptr, nullptr};
static FEField field_ase_sample_rate  = {"Sample Rate", 0, false, nullptr, nullptr};
static FEField field_data_reference_index  = {"Data Reference Index", 0, false, nullptr, nullptr};

FEField *get_top_field(){
    return &field_mp4;
}

FEField *boxField(char *type) {
    FEField *field = &field_box;
    if (!strncmp(type, "ftyp", 4)) {
        field = &field_ftyp;
    } else if (!strncmp(type, "moov", 4)) {
        field = &field_moov;
    } else if (!strncmp(type, "mvhd", 4)) {
        field = &field_mvhd;
    } else if (!strncmp(type, "iods", 4)) {
        field = &field_iods;
    } else if (!strncmp(type, "trak", 4)) {
        field = &field_trak;
    } else if (!strncmp(type, "tkhd", 4)) {
        field = &field_tkhd;
    } else if (!strncmp(type, "mdia", 4)) {
        field = &field_mdia;
    } else if (!strncmp(type, "mdhd", 4)) {
        field = &field_mdhd;
    } else if (!strncmp(type, "hdlr", 4)) {
        field = &field_hdlr;
    } else if (!strncmp(type, "minf", 4)) {
        field = &field_minf;
    } else if (!strncmp(type, "vmhd", 4)) {
        field = &field_vmhd;
    } else if (!strncmp(type, "smhd", 4)) {
        field = &field_smhd;
    } else if (!strncmp(type, "dinf", 4)) {
        field = &field_dinf;
    } else if (!strncmp(type, "dref", 4)) {
        field = &field_dref;
    } else if (!strncmp(type, "stbl", 4)) {
        field = &field_stbl;
    } else if (!strncmp(type, "stsd", 4)) {
        field = &field_stsd;
    } else if (!strncmp(type, "stts", 4)) {
        field = &field_stts;
    } else if (!strncmp(type, "stss", 4)) {
        field = &field_stss;
    } else if (!strncmp(type, "stsc", 4)) {
        field = &field_stsc;
    } else if (!strncmp(type, "stsz", 4)) {
        field = &field_stsz;
    } else if (!strncmp(type, "stco", 4)) {
        field = &field_stco;
    } else if (!strncmp(type, "udta", 4)) {
        field = &field_udta;
    } else if (!strncmp(type, "meta", 4)) {
        field = &field_meta;
    } else if (!strncmp(type, "ilst", 4)) {
        field = &field_ilst;
    } else if (!strncmp(type, "mdat", 4)) {
        field = &field_mdat;
    } else if (!strncmp(type, "url", 3)) {
        field = &field_url;
    } else if (!strncmp(type, "urn", 3)) {
        field = &field_urn;
    } else if (!strncmp(type, "avc1", 4)) {
        field = &field_avc1;
    } else if (!strncmp(type, "mp4a", 4)) {
        field = &field_mp4a;
    } else if (!strncmp(type, "avcC", 4)) {
        field = &field_avcc;
    } else if (!strncmp(type, "esds", 4)) {
        field = &field_esds;
    }
    return field;
}

int dissectSubBoxes(FERange range, FEList *nodes, int offset) {
    FERange sub;
    auto data = fetchFERangeByte(&range, offset, 8);
    while(data) {
        int size = ntohl(*(uint32_t *)data);
        FEField *field = boxField(data + 4);
        subFERangeByte(&range, &sub, offset, size);
        allocFENode(nodes, field, sub);

        offset += size;
        data = fetchFERangeByte(&range, offset, 8);
    }
    return 0;
}

int64_t dissectMP4(FERange range, void *context, FEList *nodes) {
    return dissectSubBoxes(range, nodes, 0);
}

int64_t dissectBoxHeader(FERange range, FEList *nodes, int *offset, bool full) {
    FERange sub;
    char *data = nullptr;
    int header_offset = 0;

    FETCH_FIELD(range, header_offset, 4, data, sub);
    auto node = allocFENode(nodes, &field_box_size, sub);
    translateFENodeUInt(node, ntohl(*(uint32_t *)data));

    FETCH_FIELD(range, header_offset, 4, data, sub);
    allocFENode(nodes, &field_box_type, sub);

    if (full) {
        FETCH_FIELD(range, header_offset, 1, data, sub);
        node = allocFENode(nodes, &field_box_version, sub);
        translateFENodeUInt(node, *data);

        FETCH_FIELD(range, header_offset, 3, data, sub);
        allocFENode(nodes, &field_box_flags, sub);
    }

    *offset = header_offset;
    return 0;
}

int64_t dissectBox(FERange range, void *context, FEList *nodes){
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;
    FETCH_FIELD(range, offset, remainFERangeByte(&range, offset), data, sub);
    allocFENode(nodes, &field_box_data, sub);
    return 0;
}

int64_t dissectFTYP(FERange range, void *context, FEList *nodes){
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_ftyp_major_brand, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_ftyp_minor_version, sub);

    int remain = remainFERangeByte(&range, offset);
    if (remain) {
        subFERangeByte(&range, &sub, offset, remain);
        allocFENode(nodes, &field_ftyp_compatible_brand, sub);
    }
    return 0;
}

int64_t dissectContainer(FERange range, void *context, FEList *nodes){
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    return dissectSubBoxes(range, nodes, offset);
}

int64_t dissectMVHD(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_creation_time, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_modification_time, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_time_scale, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_duration, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_mvhd_rate, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_hd_volume, sub);

    //reserved
    offset += 10;

    FETCH_FIELD(range, offset, 36, data, sub);
    allocFENode(nodes, &field_hd_matrix, sub);

    FETCH_FIELD(range, offset, 24, data, sub);
    allocFENode(nodes, &field_mvhd_pre_defined, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_mvhd_next_track_id, sub);
    return 0;
}

int64_t dissectTKHD(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_creation_time, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_modification_time, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_tkhd_track_id, sub);

    //reserved
    offset += 4;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_duration, sub);

    //reserved
    offset += 8;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_tkhd_layer, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_tkhd_alternate_group, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_hd_volume, sub);

    //reserved
    offset += 2;

    FETCH_FIELD(range, offset, 36, data, sub);
    allocFENode(nodes, &field_hd_matrix, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_tkhd_width, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_tkhd_height, sub);
    return 0;
}

int64_t dissectMDHD(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_creation_time, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_modification_time, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_time_scale, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hd_duration, sub);

    subFERange(&range, &sub, offset << 3, 1);
    allocFENode(nodes, &field_pad, sub);

    subFERange(&range, &sub, (offset << 3) + 1, 15);
    offset += 2;
    allocFENode(nodes, &field_mdhd_language, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_mdhd_quality, sub);
    return 0;
}

int64_t dissectHDLR(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    //reserved
    offset += 4;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_hdlr_type, sub);

    //reserved
    offset += 12;

    subFERangeByte(&range, &sub, offset, remainFERangeByte(&range, offset));
    allocFENode(nodes, &field_hdlr_name, sub);
    return 0;
}

int64_t dissectVMHD(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_vmhd_graphics_mode, sub);

    subFERangeByte(&range, &sub, 6, remainFERangeByte(&range, offset));
    allocFENode(nodes, &field_vmhd_op_color, sub);
    return 0;
}

int64_t dissectSMHD(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_smhd_banlance, sub);
    return 0;
}

int64_t dissectDREF(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_entry_count, sub);

    return dissectSubBoxes(range, nodes, offset);
}

int64_t dissectURI(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    uint64_t remain = remainFERangeByte(&range, offset);
    if (remain) {
        subFERangeByte(&range, &sub, offset, remain);
        allocFENode(nodes, &field_uri_location, sub);
    }
    return 0;
}

int64_t dissectSTSD(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_entry_count, sub);

    return dissectSubBoxes(range, nodes, offset);
}

int64_t dissectAVC1(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    offset += 6;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_data_reference_index, sub);

    offset += 16;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_vse_width, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_vse_heigh, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_vse_horizontal_resolution, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_vse_vertical_resolution, sub);

    offset += 4;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_vse_frame_count, sub);

    FETCH_FIELD(range, offset, 32, data, sub);
    allocFENode(nodes, &field_vse_compressor_name, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_vse_depth, sub);

    offset += 2;

    return dissectSubBoxes(range, nodes, offset);
}

int64_t dissectMP4A(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    offset += 6;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_data_reference_index, sub);

    offset += 8;

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_ase_channel_count, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    allocFENode(nodes, &field_ase_sample_size, sub);

    offset += 4;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_ase_sample_rate, sub);

    return dissectSubBoxes(range, nodes, offset);
}

int64_t dissectAVCC(FERange range, void *context, FEList *nodes){
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_configuration_version, sub);

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_profile_indication, sub);

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_profile_compatibility, sub);

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_level_indication, sub);

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_length_size, sub);

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_number_sps, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    int sps_length = ntohs(*(uint16_t *)data);
    allocFENode(nodes, &field_avcc_sps_length, sub);

    FETCH_FIELD(range, offset, sps_length, data, sub);
    allocFENode(nodes, &field_avcc_sps, sub);

    FETCH_FIELD(range, offset, 1, data, sub);
    allocFENode(nodes, &field_avcc_number_pps, sub);

    FETCH_FIELD(range, offset, 2, data, sub);
    int pps_length = ntohs(*(uint16_t *)data);
    allocFENode(nodes, &field_avcc_pps_length, sub);

    FETCH_FIELD(range, offset, pps_length, data, sub);
    allocFENode(nodes, &field_avcc_pps, sub);
    return 0;
}

int64_t dissectESDS(FERange range, void *context, FEList *nodes){
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, false) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;
    //todo
    return 0;
}

int64_t dissectSTSS(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_entry_count, sub);

    int index = 0;
    int entry_count = ntohl(*(uint32_t *)data);
    while (entry_count) {
        FETCH_FIELD(range, offset, 4, data, sub);
        auto node = allocFENode(nodes, &field_stss_sample_number, sub);
        indexFENode(node, index, -1, -1);
        --entry_count;
        ++index;
    }

    return 0;
}

int64_t dissectSTSZ(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_stsz_sample_size, sub);
    int sample_size = ntohl(*(uint32_t *)data);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_stsz_sample_count, sub);

    int index = 0;
    int sample_count = ntohl(*(uint32_t *)data);
    if(!sample_size) {
        while (sample_count) {
            FETCH_FIELD(range, offset, 4, data, sub);
            auto node = allocFENode(nodes, &field_stsz_entry_size, sub);
            indexFENode(node, index, -1, -1);
            --sample_count;
            ++index;
        }
    }
    return 0;
}

int64_t dissectSTCO(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_entry_count, sub);

    int index = 0;
    int entry_count = ntohl(*(uint32_t *)data);
    while (entry_count) {
        FETCH_FIELD(range, offset, 4, data, sub);
        auto node = allocFENode(nodes, &field_stco_chunk_offset, sub);
        indexFENode(node, index, -1, -1);
        --entry_count;
        ++index;
    }
    return 0;
}

int64_t dissectSTSC(FERange range, void *context, FEList *nodes) {
    int offset = 0;
    if (dissectBoxHeader(range, nodes, &offset, true) < 0){
        return -1;
    }

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_entry_count, sub);

    int index = 0;
    int entry_count = ntohl(*(uint32_t *)data);
    while (entry_count) {
        FETCH_FIELD(range, offset, 12, data, sub);
        auto node = allocFENode(nodes, &field_stsc_entry, sub);
        indexFENode(node, index, -1, -1);
        --entry_count;
        ++index;
    }
    return 0;
}

int64_t dissectSTSCEntry(FERange range, void *context, FEList *nodes) {
    int offset = 0;

    FERange sub;
    char *data = nullptr;

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_stsc_first_chunk, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_stsc_samples_per_chunk, sub);

    FETCH_FIELD(range, offset, 4, data, sub);
    allocFENode(nodes, &field_stsc_sample_description_index, sub);
    return 0;
}
